#ifndef __NURBS_ELEMENT__
#define __NURBS_ELEMENT__

#include "simplex.h"
#include "node.h"
#include "dispatcher.h"

struct ElementInterface {
	virtual void Evaluate2(nfloat& x, const Vector& P, size_t attributeHandle) const = 0;
	virtual void Evaluate2(Vec3n& x, const Vector& P, size_t attributeHandle) const = 0;
	virtual void Evaluate2(Vec4n& x, const Vector& P, size_t attributeHandle) const = 0;
	virtual void Integrate2(nfloat& x, const Node* node, size_t attributeHandle) const = 0;
	virtual void Integrate2(Vec3n& x, const Node* node, size_t attributeHandle) const = 0;
};

class Element : public Simplex, protected ElementInterface {
public:
	Element(Attribute& attribute, size_t id) : Simplex(attribute, id) {}
	template<typename T> T Evaluate(const Vector& P, const std::string& attribute) const;
	template<typename T> T Evaluate(const Vector& P, size_t attributeHandle) const;
	template<typename T> T Integrate(const Node* node, const std::string& attribute) const;
	template<typename T> T Integrate(const Node* node, size_t attributeHandle) const;

	template<typename T, unsigned D> T Evaluate(const math::Vec<nfloat, D>& P, const std::string& attribute) const;
	template<typename T> T Evaluate(const nfloat& P, const std::string& attribute) const {return Evaluate<T, 1>(math::V1n(P), attribute);}
	template<typename T> T Evaluate(const Vec2n& P, const std::string& attribute) const {return Evaluate<T, 2>(P, attribute);}
	template<typename T> T Evaluate(const Vec3n& P, const std::string& attribute) const {return Evaluate<T, 3>(P, attribute);}

	template<typename T, unsigned D> T Evaluate(const math::Vec<nfloat, D>& P, size_t attributeHandle) const;
	template<typename T> T Evaluate(const nfloat& P, size_t attributeHandle) const {return Evaluate<T, 1>(math::V1n(P), attributeHandle);}
	template<typename T> T Evaluate(const Vec2n& P, size_t attributeHandle) const {return Evaluate<T, 2>(P, attributeHandle);}
	template<typename T> T Evaluate(const Vec3n& P, size_t attributeHandle) const {return Evaluate<T, 3>(P, attributeHandle);}
	
	virtual const Node* GetNode(size_t i) const {return 0;}
	virtual Node* GetNode(size_t i) {return 0;}

	virtual Matrix Stiffness(const std::string& attribute) const {return Matrix(0, 0);}
	virtual Matrix Mass(const std::string& attribute, const bool lumped) const {return Matrix(0, 0);}
	
	virtual size_t SizeNodes() const {return 0;}

	virtual void Dispatch(Dispatcher* d) {
		
	}
	bool Exist(const std::string& attribute) const {
		return GetNode(0)->Exist(attribute);
	}
	
	virtual std::pair<Matrix, nfloat> Force(const std::string& materialAttribute, const std::string& positionAttribute) const {return std::pair<Matrix, nfloat>(Matrix(0, 0), 0.0);}
	virtual Matrix Stiffness(const bool nonlinear, const std::string& materialAttribute, const std::string& positionAttribute) const {return Matrix(0, 0);}
	virtual Matrix Mass(const nfloat density, const std::string& attribute) const{return Matrix(0, 0);}
	virtual nfloat Energy(const std::string& materialAttribute, const std::string& positionAttribute) const {return 0.0;}
	virtual Vector Force(const std::string& attribute) const {return Vector(0);}
	virtual bool Rotation(Matrix& R, const std::string& materialAttribute, const std::string& positionAttribute) const {return false;}
protected:
	size_t AttributeHandle(const std::string& attribute) const {
		return GetNode(0)->GetAttribute()->Handle(attribute);
	}
};

template<typename T>
T Element::Evaluate(const Vector& P, const std::string& attribute) const {
	size_t attributeHandle = AttributeHandle(attribute);
	return Evaluate<T>(P, attributeHandle);
}

template<typename T, unsigned D>
T Element::Evaluate(const math::Vec<nfloat, D>& P, const std::string& attribute) const {
	Vector P2(D);
	for(size_t i = 0; i < D; i++)
		P2(i) = P[i];
	return Evaluate<T>(P2, attribute);
}

template<typename T, unsigned D>
T Element::Evaluate(const math::Vec<nfloat, D>& P, size_t attributeHandle) const {
	Vector P2(D);
	for(size_t i = 0; i < D; i++)
		P2(i) = P[i];
	return Evaluate<T>(P2, attributeHandle);
}

template<typename T>
T Element::Evaluate(const Vector& P, size_t attributeHandle) const {
	T x(0.0);
	Evaluate2(x, P, attributeHandle);
	return x;
}

template<typename T>
T Element::Integrate(const Node* node, const std::string& attribute) const {
	size_t attributeHandle = AttributeHandle(attribute);
	return Integrate<T>(node, attributeHandle);
}

template<typename T>
T Element::Integrate(const Node* node, size_t attributeHandle) const {
	T x(0.0);
	Integrate2(x, node, attributeHandle);
	return x;
}

template<typename Base>
struct ElementImplementation : public Element {
	ElementImplementation(Attribute& attribute, size_t id) : Element(attribute, id) {}
	virtual void Evaluate2(nfloat& x, const Vector& P, size_t attributeHandle) const {self()->EvaluateImplementation(x, P, attributeHandle);}
	virtual void Evaluate2(Vec3n& x, const Vector& P, size_t attributeHandle) const {self()->EvaluateImplementation(x, P, attributeHandle);}
	virtual void Evaluate2(Vec4n& x, const Vector& P, size_t attributeHandle) const {self()->EvaluateImplementation(x, P, attributeHandle);}
	virtual void Integrate2(nfloat& x, const Node* node, size_t attributeHandle) const {self()->IntegrateImplementation(x, node, attributeHandle);}
	virtual void Integrate2(Vec3n& x, const Node* node, size_t attributeHandle) const {self()->IntegrateImplementation(x, node, attributeHandle);};
	
private:
	Base* self() 
	{
		return static_cast<Base*>(this);
	}
	const Base* self() const
	{
		return static_cast<const Base*>(this);
	}
};

#endif //__NURBS_ELEMENT__
